/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package flightlifter;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;

/**
 *
 * @author Matt
 */
public class PerspectiveCamera extends Camera {

    int windowWidth = 0;
    int windowHeight = 0;
    public static final int ORBIT = 1;
    public static final int MOUSELOOK = 2;
    public static final int ZOOM = 3;
    public static final int MOVE = 4;
    public static final int WSAD = 5;
    public static final int DOLLY = 6;
    public PVector target;
    public PVector up, right, forward;
    public float rho, phi = .5f, theta = 0.5f;
    private float roll = 0;
    private int mode = ORBIT;
    Selectable lookAt = null;

    public PerspectiveCamera(float x, float y, float z, int vx, int vy, int vw, int vh) {
        pos = new PVector(x, y, z);
        target = new PVector(0, 0, 0);
        this.vx = vx;
        this.vy = vy;

        this.vw = vw;
        this.vh = vh;
        validateSpherical();

    }

    public void camera() {

        GL11.glViewport(vx, vy, vw, vh);
        GL11.glMatrixMode(GL11.GL_PROJECTION); // Select The Projection Matrix
        GL11.glLoadIdentity(); // Reset The Projection Matrix

        GLU.gluPerspective(
                45.0f,
                (float) vw / vh,
                1f,
                40000.0f);


        GL11.glMatrixMode(GL11.GL_MODELVIEW); // Select The Modelview Matrix
        GL11.glLoadIdentity();

        if (lookAt == null) {
            
            GLU.gluLookAt(pos.x, pos.y, pos.z,
                    target.x, target.y, target.z,
                    0, 1, 0);


        } else {
            PVector cent = lookAt.getCentroid();

            GLU.gluLookAt(cent.x + pos.x, cent.y + pos.y, cent.z + pos.z,
                    cent.x, cent.y, cent.z,
                   0,1,0);
        }


        GLProject.getMatrices();
    }

    public void setLookAt(Selectable n) {
        lookAt = n;
    }

    public float screenToWorldX(int x) {
        float ret = 0;
        return ret;
    }

    public float screenToWorldY(int y) {
        float ret = 0;
        return ret;
    }

    public Ray getRay(int mouseX, int mouseY) {

        GL11.glPushMatrix();
        camera();

        IntBuffer viewport = BufferUtils.createIntBuffer(16);
        FloatBuffer modelview = BufferUtils.createFloatBuffer(16);
        FloatBuffer projection = BufferUtils.createFloatBuffer(16);
        FloatBuffer winZ = BufferUtils.createFloatBuffer(1);
        float winX, winY, wz;
        FloatBuffer position = BufferUtils.createFloatBuffer(3);

        GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, modelview);
        GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, projection);
        GL11.glGetInteger(GL11.GL_VIEWPORT, viewport);

        winX = (float) mouseX;
        winY = (float) mouseY;

        GL11.glReadPixels(mouseX, (int) winY, 1, 1, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, winZ);
        wz = winZ.get();
        System.out.println(wz);
        GLU.gluUnProject(winX, winY, wz, modelview, projection, viewport, position);

        PVector dir = new PVector(position.get(0) - pos.x, position.get(1) - pos.y, position.get(2) - pos.z);
        dir.normalize();

        GL11.glPopMatrix();
        return new Ray(pos.get(), dir, 2 * wz * FlightLifter.ZDIST);



    }

    public void rotY(float rads) {

        theta += rads;

        validate();

    }

    public void lookZ(float amt) {

        phi += amt;

        //singularities at the poles, so don't let the values actually become them
        //phi = PApplet.constrain(phi, minPhi, maxPhi);
        validate();

    }

    private void validateSpherical() {

        //standard conversion from cartesian to spherical
        PVector look = PVector.sub(target, pos);

        rho = look.mag();
        theta = (float) (Math.atan2(look.z, look.x));
        phi = (float) (Math.acos(look.y / rho));
        setUpVector();
    }

    public void setUpVector() {
        up = new PVector(0, 1, 0);

        right = new PVector();
        forward = PVector.sub(target, pos);
        forward.normalize();
        //set the right vector by crossing the up and forward vectors.
        forward.cross(up, right);
        right.normalize();
        right.cross(forward, up);
    }

    /**
     * Generate the cartesian coordinates from the spherical ones.
     */
    public void validate() {


        //convert from spherical to cartesian
        float tx = (float) (rho * Math.sin(phi) * Math.cos(theta));
        float tz = (float) (rho * Math.sin(phi) * Math.sin(theta));
        float ty = (float) (rho * Math.cos(phi));

        if (mode == MOUSELOOK) {
            target = PVector.add(pos, new PVector(tx, ty, tz));
        }
        else if (mode == ORBIT || mode == DOLLY) {
            tx = -tx;
            ty = -ty;
            tz = -tz;
            pos = PVector.add(target, new PVector(tx, ty, tz));
        }

        setUpVector();
    }

    public void orbit(int dx, int dy) {
        rotY(dx / 100.f);
        lookZ(dy / -100.f);
    }

    public void zoom(float amt){

        rho *= amt;
        validate();
    }

    public void translate(float x, float y){
        x *= rho/1000.f;
        y *= rho/1000.f;

        pos = PVector.add(pos,PVector.mult(up, -y));
        pos = PVector.add(pos,PVector.mult(right, -x));

        target = PVector.add(target,PVector.mult(up, -y));
        target = PVector.add(target,PVector.mult(right, -x));

        validateSpherical();
    }

    @Override
    public PVector getUp() {
        return up.get();
    }

    @Override
    public PVector getRight() {
        return right.get();
    }
}
